﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Twodi.Interfaces;
using Twodi.Aspect.Interfaces;
using Twodi.Behavior.Implemented;
using System.ComponentModel;
using Microsoft.Xna.Framework.Graphics;

namespace Twodi.Implemented
{
    /// <summary>
    /// Tipos de Alinhamentos dos itens.
    /// </summary>
    public enum MenuItemAlign
    {
        Left,
        Center,
        Right
    }

    /// <summary>
    /// Orientação dos itens do menu.
    /// </summary>
    public enum MenuOrientation
    {
        Horizontal,
        Vertical
    }

    public class Menu<T> : AbstractManagedContainer<T> where T : AbstractRenderable
    {
        /// <summary>
        /// Alinhamento dos Itens do menu.
        /// </summary>
        protected MenuItemAlign align = MenuItemAlign.Center;

        /// <summary>
        /// Orientação dos Itens do menu.
        /// </summary>
        protected MenuOrientation orientation = MenuOrientation.Vertical;

        /// <summary>
        /// Alterar tamanho dos itens ao criar.
        /// </summary>
        protected bool resizeItems = false;

        /// <summary>
        /// Define tamanho máximo do item durante sua criação, caso seja informado para alterar tamanho dos itens.
        /// </summary>
        protected Vector2 itemMaxSize = new Vector2(50, 50);

        protected int leftSpacing;

        protected int rightSpacing;

        protected int topSpacing;

        protected int bottomSpacing;

        protected int betweenSpacing;

        public Menu(Game game)
            : base(game)
        {
            this.Components.ListChanged += (sender, e) =>
            {
                DefineMenu();
            };
        }

        /// <summary>
        /// Adicionar componentes ao menu.
        /// Caso componente não possua SpriteBatch, e o Menu possua, é definido o SpriteBatch do Componente com o do Menu.
        /// </summary>
        /// <param name="components"></param>
        public override void Add(params T[] components)
        {
            if (SpriteBatch != null)
            {
                for (int i = 0; i < components.Length; i++)
                {
                    if (components[i].SpriteBatch == null)
                    {
                        components[i].SpriteBatch = this.SpriteBatch;
                    }
                }
            }
            base.Add(components);
        }

        /// <summary>
        /// Ordenar itens do menu e calcular seu tamanho.
        /// </summary>
        protected virtual void DefineMenu()
        {
            Vector2 menuSize = new Vector2(leftSpacing + rightSpacing, topSpacing + bottomSpacing);
            Vector2 itemPosition = this.Position;

            for (int i = 0; i < Components.Count; i++)
            {
                T component = Components[i];

                if (AutoRescale)
                {
                    RescaleComponent(component);
                }

                if (Orientation == MenuOrientation.Vertical)
                {
                    if (component.Size.X > menuSize.X)
                    {
                        menuSize.X = component.Size.X;
                    }

                    menuSize.Y += component.Size.Y;
                    if (i != 0)
                    {
                        itemPosition.Y = Components[i - 1].Bounds.Bottom;
                    }
                }
                else if (Orientation == MenuOrientation.Horizontal)
                {
                    if (component.Size.Y > menuSize.Y)
                    {
                        menuSize.Y = component.Size.Y;
                    }

                    menuSize.X += component.Size.X;

                    if (i != 0)
                    {
                        itemPosition.X = (Components[i - 1] as AbstractRenderable).Bounds.Right;
                    }
                }

                component.Position = itemPosition + component.Origin;

                if (Orientation == MenuOrientation.Vertical)
                {
                    switch (Align)
                    {
                        case MenuItemAlign.Center:
                            component.Position += new Vector2((menuSize.X - component.Size.X) / 2, 0);
                            break;
                        case MenuItemAlign.Right:
                            component.Position += new Vector2((menuSize.X - component.Size.X), 0);
                            break;
                        default:
                            break;
                    }
                }
            }

            this.Size = menuSize;
        }

        /// <summary>
        /// Calcular nova escala do componente para ficar igual ao tamanho máximo do item.
        /// </summary>
        /// <param name="component">Componente a ser alterado escala.</param>
        protected virtual void RescaleComponent(T component)
        {
            float itemScale = component.Scale;

            if (component.Size.X > component.Size.Y && component.Size.X > itemMaxSize.X)
            {
                itemScale = itemMaxSize.X / component.Size.X;
            }
            else if (component.Size.Y > component.Size.X && component.Size.Y > itemMaxSize.Y)
            {
                itemScale = itemMaxSize.Y / component.Size.Y;
            }

            if (component.Scale != itemScale)
            {
                component.Scale = itemScale;
            }
        }

        /// <summary>
        /// Alinhamento dos Itens do menu.
        /// </summary>
        public MenuItemAlign Align
        {
            get { return this.align; }
            set
            {
                this.align = value;
                onAlignChanged(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Alterar tamanho dos itens ao criar.
        /// </summary>
        public bool AutoRescale
        {
            get { return this.resizeItems; }
            set
            {
                this.resizeItems = value;
                onResizeItemsChanged(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Define tamanho máximo do item durante sua criação, caso seja informado para alterar tamanho dos itens.
        /// </summary>
        public Vector2 ItemMaxSize
        {
            get { return this.itemMaxSize; }
            set
            {
                this.itemMaxSize = value;
                onItemMaxSizeChanged(EventArgs.Empty);
            }
        }

        public int LeftSpacing
        {
            get { return this.leftSpacing; }
            set
            {
                this.leftSpacing = value;
                onLeftSpacingChanged(EventArgs.Empty);
            }
        }

        public int RightSpacing
        {
            get { return this.rightSpacing; }
            set
            {
                this.rightSpacing = value;
                onRightSpacingChanged(EventArgs.Empty);
            }
        }

        public int TopSpacing
        {
            get { return this.topSpacing; }
            set
            {
                this.topSpacing = value;
                onTopSpacingChanged(EventArgs.Empty);
            }
        }

        public int BottomSpacing
        {
            get { return this.bottomSpacing; }
            set
            {
                this.bottomSpacing = value;
                onBottomSpacingChanged(EventArgs.Empty);
            }
        }

        public int BetweenSpacing
        {
            get { return this.betweenSpacing; }
            set
            {
                this.betweenSpacing = value;
                onBetweenSpacingChanged(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Orientação dos Itens do menu.
        /// </summary>
        public MenuOrientation Orientation
        {
            get { return this.orientation; }
            set
            {
                this.orientation = value;
                onOrientationChanged(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Evento invocado caso alinhamento dos itens do menu for alterado.
        /// </summary>
        public event EventHandler<EventArgs> AlignChanged;

        /// <summary>
        /// Evento invocado caso alterar itens automaticamente for alterado.
        /// </summary>
        public event EventHandler<EventArgs> ResizeItemsChanged;

        /// <summary>
        /// Evento invocado caso alterar tamanho maximo dos itens for alterado.
        /// </summary>
        public event EventHandler<EventArgs> ItemMaxSizeChanged;

        public event EventHandler<EventArgs> LeftSpacingChanged;

        public event EventHandler<EventArgs> RightSpacingChanged;

        public event EventHandler<EventArgs> TopSpacingChanged;

        public event EventHandler<EventArgs> BottomSpacingChanged;

        public event EventHandler<EventArgs> BetweenSpacingChanged;

        public event EventHandler<EventArgs> OrientationChanged;

        /// <summary>
        /// Método invocado caso alinhamento dos itens do menu for alterado.
        /// </summary>
        protected virtual void onAlignChanged(EventArgs e)
        {
            if (AlignChanged != null)
            {
                AlignChanged(this, e);
            }
        }

        /// <summary>
        /// Método invocado caso alterar itens automaticamente for alterado.
        /// </summary>
        protected virtual void onResizeItemsChanged(EventArgs e)
        {
            if (ResizeItemsChanged != null)
            {
                ResizeItemsChanged(this, e);
            }
        }

        /// <summary>
        /// Método invocado caso alterar tamanho maximo dos itens for alterado.
        /// </summary>
        protected virtual void onItemMaxSizeChanged(EventArgs e)
        {
            if (ItemMaxSizeChanged != null)
            {
                ItemMaxSizeChanged(this, e);
            }
        }

        protected virtual void onLeftSpacingChanged(EventArgs e)
        {
            if (LeftSpacingChanged != null)
            {
                LeftSpacingChanged(this, e);
            }
        }

        protected virtual void onRightSpacingChanged(EventArgs e)
        {
            if (RightSpacingChanged != null)
            {
                RightSpacingChanged(this, e);
            }
        }

        protected virtual void onTopSpacingChanged(EventArgs e)
        {
            if (TopSpacingChanged != null)
            {
                TopSpacingChanged(this, e);
            }
        }

        protected virtual void onBottomSpacingChanged(EventArgs e)
        {
            if (BottomSpacingChanged != null)
            {
                BottomSpacingChanged(this, e);
            }
        }

        protected virtual void onBetweenSpacingChanged(EventArgs e)
        {
            if (BetweenSpacingChanged != null)
            {
                BetweenSpacingChanged(this, e);
            }
        }

        protected virtual void onOrientationChanged(EventArgs e)
        {
            if (OrientationChanged != null)
            {
                OrientationChanged(this, e);
            }
        }
    }
}
